<template>
  <custom-card class="pro-table_query-form" :is-card="isCard">
    <n-form
      ref="formRef"
      label-placement="left"
      :label-align="labelAlign"
      :label-width="labelWidth"
      v-bind="formPorps"
    >
      <n-grid
        class="query-form-grid"
        :x-gap="24"
        cols="12 s:12 m:18 l:24 xl:24"
        :collapsed="
          query.length > (firstLineQueryCount || 3) ? collapsed : false
        "
        :collapsed-rows="1"
        responsive="screen"
        v-bind="gridProps"
      >
        <template v-for="(item, _) in queryInputs" :key="item.key">
          <n-gi v-if="item.type !== 'daterange'" :span="item.span || 6">
            <n-form-item :label="item.title" :labelWidth="item.labelWidth">
              <n-input
                v-if="item.type === 'input'"
                v-model:value="form[item.key]"
                :placeholder="item.placeholder || '请输入' + item.title"
                clearable
                :allow-input="onlyAllowNumber(item.number)"
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                v-bind="item.props"
              ></n-input>

              <n-input
                v-if="item.type === 'range'"
                :value="[form[item.key[0]], form[item.key[1]]]"
                pair
                separator="-"
                :placeholder="['从', '至']"
                clearable
                :allow-input="onlyAllowNumber(item.number)"
                @update:value="
                  (value) => {
                    form[item.key[0]] = value[0]
                    form[item.key[1]] = value[1]
                    handleChange(value[0], item)
                    handleChange(value[1], item)
                  }
                "
                @keyup.enter="handleSearch"
                v-bind="item.props"
              ></n-input>

              <n-input-number
                v-if="item.type === 'inputNumber'"
                v-model:value="form[item.key]"
                :placeholder="item.placeholder || '请输入' + item.title"
                clearable
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                v-bind="item.props"
              ></n-input-number>

              <n-select
                v-if="item.type === 'select'"
                v-model:value="form[item.key as string]"
                :placeholder="item.placeholder || '请选择' + item.title"
                :options="getSelectOptions(item)"
                :value-field="item.valueCode"
                :label-field="item.labelCode"
                filterable
                clearable
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                v-bind="item.props"
              ></n-select>

              <n-tree-select
                v-if="item.type === 'treeSelect'"
                v-model:value="form[item.key as string]"
                :placeholder="item.placeholder || '请选择' + item.title"
                :options="getSelectOptions(item)"
                :key-field="item.keyCode"
                :label-field="item.labelCode"
                filterable
                clearable
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                v-bind="item.props"
              ></n-tree-select>

              <n-cascader
                v-if="item.type === 'cascader'"
                v-model:value="form[item.key as string]"
                :placeholder="item.placeholder || '请选择' + item.title"
                :options="getSelectOptions(item)"
                expand-trigger="click"
                maxTagCount="responsive"
                filterable
                clearable
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                v-bind="item.props"
              ></n-cascader>

              <n-date-picker
                v-if="item.type === 'date'"
                v-model:formatted-value="form[item.key]"
                :placeholder="item.placeholder || '请选择' + item.title"
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                valueFormat="yyyy-MM-dd"
                type="date"
                clearable
                v-bind="item.props"
              ></n-date-picker>

              <n-date-picker
                v-if="item.type === 'year'"
                v-model:formatted-value="form[item.key]"
                :placeholder="item.placeholder || '请选择' + item.title"
                @update:value="handleChange($event, item)"
                @keyup.enter="handleSearch"
                valueFormat="yyyy"
                type="year"
                clearable
                v-bind="item.props"
              ></n-date-picker>

              <component
                v-if="item.type === 'custom'"
                :is="customRender(item)"
              />
            </n-form-item>
          </n-gi>
          <template v-else>
            <n-gi :span="item.span || 6">
              <n-form-item
                :label="item.title + '起始日期'"
                :labelWidth="item.labelWidth"
              >
                <n-date-picker
                  v-model:formatted-value="form[item.key[0]]"
                  :placeholder="item.placeholder || '请选择起始日期'"
                  @update:value="handleChange($event, item, 'start')"
                  @keyup.enter="handleSearch"
                  valueFormat="yyyy-MM-dd HH:mm:ss"
                  clearable
                  type="datetime"
                  v-bind="item.props"
                ></n-date-picker>
              </n-form-item>
            </n-gi>
            <n-gi :span="item.span || 6">
              <n-form-item
                :label="item.title + '结束日期'"
                :labelWidth="item.labelWidth"
              >
                <n-date-picker
                  v-model:formatted-value="form[item.key[1]]"
                  :placeholder="item.placeholder || '请选择结束日期'"
                  @update:value="handleChange($event, item, 'end')"
                  @keyup.enter="handleSearch"
                  valueFormat="yyyy-MM-dd HH:mm:ss"
                  clearable
                  type="datetime"
                  v-bind="item.props"
                ></n-date-picker>
              </n-form-item>
            </n-gi>
          </template>
        </template>

        <n-gi suffix class="suffix" :span="5">
          <div style="width: 100%; margin-bottom: 15px">
            <n-button type="primary" @click="handleSearch()">
              <icons name="i-search" fill="#fff" />
              <span style="color: #fff">{{ props.searchBtnText }}</span>
            </n-button>
            <n-button style="margin: 0 10px" @click="handleReset()">
              <i-refresh />
              {{ props.resetBtnText }}
            </n-button>

            <n-button
              v-if="query.length > (firstLineQueryCount || 3)"
              text
              type="primary"
              @click="handleOpen"
            >
              <span v-if="collapsed">
                展开
                <i-down />
              </span>
              <span v-else>
                收起
                <i-up />
              </span>
            </n-button>
          </div>
        </n-gi>
      </n-grid>
    </n-form>
  </custom-card>
</template>

<script lang="ts" setup>
  import { PropType } from 'vue'
  import { cloneDeep, debounce, forIn, isArray, isFunction } from 'lodash-es'
  import { LabelAlign } from 'naive-ui/es/form/src/interface'
  import { ProTableQueryItem } from '../../type'
  import { filterNull } from '../../utils'
  import CustomCard from '../../components/custom-card/index.vue'

  const emits = defineEmits([
    'queryChange',
    'search',
    'reset',
    'collapsedChange',
    'update:queryForm'
  ])
  const props = defineProps({
    query: {
      type: Array as PropType<ProTableQueryItem[]>,
      default: () => []
    },
    labelWidth: Number as PropType<string | number>,
    labelAlign: String as PropType<LabelAlign>,
    defaultCollapsed: {
      type: Boolean,
      default: true
    },
    searchBtnText: {
      type: String,
      default: '查询'
    },
    resetBtnText: {
      type: String,
      default: '重置'
    },
    autoSearch: {
      type: Boolean,
      default: false
    },
    isCard: {
      type: Boolean,
      default: true
    },
    firstLineQueryCount: Number,
    formPorps: {
      type: Object,
      default: () => ({})
    },
    gridProps: {
      type: Object,
      default: () => ({})
    },
    // 双向绑定的query-form
    queryForm: {
      type: Object,
      default: () => ({})
    }
  })

  const getInitQueryForm = () => {
    const initQueryForm: Record<string | number | symbol, any> = {}
    props.query.forEach((item) => {
      if (item.type === 'range' || item.type === 'daterange') {
        const [startValue, endValue] = Array.isArray(item.defaultValue)
          ? item.defaultValue
          : [null, null]
        initQueryForm[item.key[0]] = startValue
        initQueryForm[item.key[1]] = endValue
      } else if (item.type === 'custom') {
        if (Array.isArray(item.key)) {
          item.key.forEach((key) => {
            initQueryForm[key] = null
          })
        } else {
          initQueryForm[item.key] = null
        }
      } else {
        initQueryForm[item.key] =
          item.defaultValue || (item.defaultValue === false ? false : null)
      }
    })
    emits('search', filterNull(initQueryForm))
    return initQueryForm
  }

  const [form, resetForm] = useProRef<Record<string, any>>(getInitQueryForm())
  const { query } = toRefs(props)
  const collapsed = ref(props.defaultCollapsed)
  const firstLineCount = ref(props.firstLineQueryCount)
  const noResetField: string[] = []

  const queryInputs = computed(() => {
    // 过滤需要隐藏的搜索项
    const inputs: ProTableQueryItem[] = cloneDeep(
      query.value.filter((i) => i.visiable !== false)
    )

    // 排序
    inputs.sort((a, b) => (a.order || 999) - (b.order || 999))

    // 设置第一行展示几个输入框
    if (firstLineCount.value) {
      inputs.forEach((item, index) => {
        if (index === firstLineCount.value && collapsed.value) {
          item.span = 24
        }
      })
    }

    // 记录不需要重置的字段
    inputs.forEach((item) => {
      if (item.noReset) {
        const fields = isArray(item.key) ? item.key : [item.key]
        noResetField.push(...fields)
      }
    })
    return inputs
  })

  const getSelectOptions = (item) => {
    return item.dictOptions || useDictData(item.dictCode).value
  }

  const customRender = (item: ProTableQueryItem) => {
    if (item.type === 'custom' && isFunction(item.render)) {
      return item.render(form)
    }
  }

  const handleSearch = (params?: Record<string, any>) => {
    if (params) {
      form.value = Object.assign({}, form.value, params)
    }
    emits('search', filterNull(form.value))
  }
  const handleReset = () => {
    // resetForm()
    const noResetValue = {}
    if (noResetField.length) {
      forIn(form.value, (value, key) => {
        if (noResetField.includes(key)) {
          noResetValue[key] = value
        }
      })
    }
    resetForm(noResetValue)
    emits('reset')
    handleSearch()
  }
  const handleOpen = () => {
    collapsed.value = !collapsed.value
    if (firstLineCount.value) {
      firstLineCount.value = collapsed.value ? props.firstLineQueryCount : -1
    }
    emits('collapsedChange', collapsed.value)
  }

  function handleChange(value, item, type?: 'start' | 'end') {
    emits('queryChange', value, item, type)
    if (props.autoSearch) {
      searchHandleDebounce()
    }
  }

  const searchHandleDebounce = debounce(() => {
    nextTick(() => {
      handleSearch()
    })
  }, 500)

  const onlyAllowNumber = (number = false) => {
    if (number) return (value) => !value || /^\d+$/.test(value)
    return () => true
  }

  // 同步更新queryForm的值
  watch(
    form,
    (val) => {
      emits('update:queryForm', val)
    },
    {
      deep: true,
      immediate: true
    }
  )
  watch(
    () => props.queryForm,
    (val) => {
      if (val !== form.value) {
        Object.assign(form.value, val)
      }
    },
    {
      deep: true
    }
  )

  defineExpose({
    search: handleSearch,
    reset: handleReset
  })
</script>

<style lang="less">
  .pro-table_query-form {
    margin-bottom: 15px;

    .n-card__content {
      padding-bottom: 0px !important;
    }

    .n-form-item {
      .n-form-item-feedback-wrapper {
        min-height: 0px;
      }
    }

    .n-form-item {
      padding: 0px 10px;
      box-sizing: border-box;
      margin-bottom: 15px;
    }

    .suffix {
      width: 100%;
      display: flex;
      text-align: right;
    }
  }
</style>
